#ifndef _AMFIO_H
#define _AMFIO_H

#include <stdint.h>
#include <setjmp.h>

enum amf0_marker {
	AMF_NUMBER = 0x00,
	AMF_BOOLEAN = 0x01,
	AMF_STRING = 0x02,
	AMF_OBJECT = 0x03,
	AMF_MOVIECLIP = 0x04,
	AMF_NULL = 0x05,
	AMF_UNDEFINED = 0x06,
	AMF_REFERENCE = 0x07,
	AMF_ECMA_ARRAY = 0x08,
	AMF_OBJECT_END = 0x09,
	AMF_STRICT_ARRAY = 0x0a,
	AMF_DATE = 0x0b,
	AMF_LONG_STRING = 0x0c,
	AMF_UNSUPPORTED = 0x0d,
	AMF_RECORDSET = 0x0e,
	AMF_XML_DOCUMENT = 0x0f,
	AMF_TYPED_OBJECT = 0x10,
	AMF_AVMPLUS_OBJECT = 0x11,
	AMF_MAX = 0x12,
};

/*
marker  =  U8 
number-marker  = 0x00 
boolean-marker  = 0x01 
string-marker  = 0x02 
object-marker  = 0x03 
null-marker  = 0x05 
undefined-marker  = 0x06 
reference-marker  = 0x07 
ecma-array-marker  = 0x08 
object-end-marker  = 0x09 
strict-array-marker  = 0x0A 
date-marker  = 0x0B 
long-string-marker  = 0x0C 
unsupported-marker  = 0x0D 
xml-document-marker  = 0x0F 
typed-object-marker  = 0x10 

movieclip-marker  = 0x04  ; reserved, not supported 
recordset-marker  = 0x0E  ; reserved, not supported 
avmplus-object-marker  = 0x11

value-type   =  number-type | boolean-type | string-type | 
		object-type | null-marker | undefined-marker | 
		reference-type | ecma-array-type |  
		strict-array-type | date-type | long-string-type 
		| xml-document-type | typed-object-type

number-type  =  number-marker DOUBLE
boolean-type  =  boolean-marker U8
string-type   =  string-marker UTF-8
long-string-type  =  long-string-marker UTF-8-long
null-type  =  null-marker
undefined-type  =  undefined-marker
reference-type  =  reference-marker U16
time-zone  =  S16  ; reserved,  not supported  should be set  to 0x0000 
date-type  =  date-marker DOUBLE time-zone

object-property  = (UTF-8 value-type) |  (UTF-8-empty object-end-marker) 
anonymous-object-type  = object-marker *(object-property) 


associative-count  =  U32 
ecma-array-type  =  associative-count *(object-property) 

object-end-type  =  UTF-8-empty object-end-marker  ; 0x00 0x00 0x09

array-count  =  U32 
strict-array-type  =  array-count *(value-type) 

class-name  =  UTF-8 
object-type  =  object-marker class-name *(objectproperty) 
*/

struct amf_ioctx {
	const const char *base;
	size_t pos, end;
	jmp_buf jmpenv;
};
static inline void writeu16(char *p, uint16_t i)
{
	unsigned char *base;
	base = (unsigned char *)p;
	base[0] = i >> 8;
	base[1] = i;
}
static inline void writeu32(char *p, uint32_t i)
{
	unsigned char *base;
	base = (unsigned char *)p;
	base[0] = i >> 24;
	base[1] = i >> 16;
	base[2] = i >> 8;
	base[3] = i;
}
static inline void writef64(char *p, double d)
{
	unsigned char *base;
	union {
		uint64_t i;
		double d;
	} u;
	u.d = d;
	base = (unsigned char *)p;
	base[0] = u.i >> 56;
	base[1] = u.i >> 48;
	base[2] = u.i >> 40;
	base[3] = u.i >> 32;
	base[4] = u.i >> 24;
	base[5] = u.i >> 16;
	base[6] = u.i >> 8;
	base[7] = u.i;
}

static inline uint8_t readu8(const char *p)
{
	return p[0];
}
static inline uint16_t readu16(const char *p)
{
	const unsigned char *base;
	base = (const unsigned char *)p;
	return (uint32_t)base[1] | ((uint32_t)base[0] << 8);
}
static inline int16_t reads16(const char *p)
{
	return readu16(p);
}
static inline uint32_t readu24(const char *p)
{
	const unsigned char *base;
	base = (const unsigned char *)p;
	return (uint32_t)base[2] | ((uint32_t)base[1] << 8) |
		((uint32_t)base[0] << 16);
}
static inline uint32_t readu32(const char *p)
{
	const unsigned char *base;
	base = (const unsigned char *)p;
	return (uint32_t)base[3] | ((uint32_t)base[2] << 8) |
		((uint32_t)base[1] << 16) | ((uint32_t)base[0] << 24);
}
static inline double readf64(const char *p)
{
	const unsigned char *base;
	union {
		uint64_t ui64;
		double d;
	} u;
	base = (const unsigned char *)p;
	u.ui64 = (uint64_t)base[7] | ((uint64_t)base[6] << 8) |
		((uint64_t)base[5] << 16) | ((uint64_t)base[4] << 24) |
		((uint64_t)base[3] << 32) | ((uint64_t)base[2] << 40) |
		((uint64_t)base[1] << 48) | ((uint64_t)base[0] << 56);
	return u.d;
}
static inline void seek(struct amf_ioctx *c, int off)
{
	if (c->pos + off > c->end)
		longjmp(c->jmpenv, -1);
	c->pos += off;
}
static inline const char *ptr(struct amf_ioctx *c)
{
	return c->base + c->pos;
}
#define DEF_PEEK_AND_GET(name, type) \
static inline type peek##name(struct amf_ioctx *c)\
{\
	seek(c, sizeof(type));\
	seek(c, -sizeof(type));\
	return read##name(c->base + c->pos);\
}\
static inline type get##name(struct amf_ioctx *c)\
{\
	seek(c, sizeof(type));\
	return read##name(c->base + (c->pos - sizeof(type)));\
}
DEF_PEEK_AND_GET(u8, uint8_t)
DEF_PEEK_AND_GET(u16, uint16_t)
DEF_PEEK_AND_GET(s16, int16_t)
DEF_PEEK_AND_GET(u24, uint32_t)
DEF_PEEK_AND_GET(u32, uint32_t)
DEF_PEEK_AND_GET(f64, double)

#endif /* _AMFIO_H */
